package Derivation;

import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import Basics.*;

public class ParseResultsBottomUp 
{
	DeriveByProgramBottomUp programDeriver;
	
	Vector<Atom> addedInstAtoms = new Vector<Atom>();
	
	Map<Atom, Vector<Vector<Atom>>> provenance = new HashMap<Atom, Vector<Vector<Atom>>>();
	
	Vector<Rule> constantsProgram = new Vector<Rule>();
	
	
	
	public ParseResultsBottomUp (Rule ... irs)
	{
		this.programDeriver = new DeriveByProgramBottomUp (irs);
	}
	
	
	
	public ParseResultsBottomUp (Program p)
	{
		this.programDeriver = new DeriveByProgramBottomUp (p);
	}


	
	public Vector<Atom> getAddedInstAtoms() 
	{
		return addedInstAtoms;
	}

	

	public Map<Atom, Vector<Vector<Atom>>> getProvenance() 
	{
		return provenance;
	}
	

	public Vector<Rule> getConstantsProgram() 
	{
		return constantsProgram;
	}



	public void setConstantsProgram(Vector<Rule> constantsProgram) 
	{
		this.constantsProgram = constantsProgram;
	}



	/*************************************************************************************************************/
	/** Title: ParseResults																				
	/** Description: finds all newly derived atoms from this step and adds them to this.addedInstAtoms			
	/*************************************************************************************************************/
	
	public void ParseResults (boolean forIntersection)
	{
		this.programDeriver.DeriveFromProgramIterationBottomUp();
		Vector<Vector<Rule>> derivationsVec = this.programDeriver.getDerivationsVec();
		for (Vector<Rule> vector : derivationsVec) 
		{
			for (Rule rule : vector) 
			{ 
				if (false == DB.getInstance().ContainedInTable(rule.getHead()))
				{
					if (false == this.addedInstAtoms.contains(rule.getHead()))
					{
						this.addedInstAtoms.add(rule.getHead());
					}
				}
				
				if (true == forIntersection) 
				{
					if (false == this.constantsProgram.contains(rule)) 
					{
						this.constantsProgram.add(rule);
					}
				}
				
				AddToProv(rule.getHead(), getProv(rule.getHead(), derivationsVec));
			}
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: AddToProv																				
	/** Description: Adds bodies to prov. if it isn't already there			
	/*************************************************************************************************************/
	
	public void AddToProv (Atom key, Vector<Vector<Atom>> bodies)
	{
		if (true == this.provenance.containsKey(key))
		{
			for (Vector<Atom> vector : bodies) 
			{
				if (false == this.provenance.get(key).contains(vector)) 
				{
					this.provenance.get(key).add(vector);
				}
			}
		}
		
		else
		{
			this.provenance.put(key, bodies);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: getProv																				
	/** Description: get provenance for each new derived inst atom			
	/*************************************************************************************************************/
	
	public Vector<Vector<Atom>> getProv(Atom head, Vector<Vector<Rule>> derivationsVec)
	{
		Vector<Vector<Atom>> prov = new Vector<Vector<Atom>>();
		for (Vector<Rule> vector : derivationsVec) 
		{
			for (Rule rule : vector) 
			{
				if (true == rule.getHead().equals(head))
				{
					prov.add(rule.getBody());
				}
			}
		}
		
		return prov;
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: UpdateDB																				
	/** Description: add newly derived facts to DB			
	/*************************************************************************************************************/
	
	public void UpdateDB ()
	{
		for (Atom atom : this.addedInstAtoms) 
		{
			DB.getInstance().Update(atom);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: Reset																				
	/** Description: resets the parameters for next step of the program			
	/*************************************************************************************************************/
	
	public void Reset ()
	{
		this.addedInstAtoms.clear();
		//this.provenance.clear();
		this.programDeriver.Reset();
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: isFixedPoint																				
	/** Description: checks if new atoms were added at this step.			
	/*************************************************************************************************************/
	
	public boolean isFixedPoint ()
	{
		return this.addedInstAtoms.isEmpty() ? true : false;
	}
}
